package com.easylinkin.linkappapi.lobar.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.common.model.RequestModel;
import com.easylinkin.linkappapi.lobar.dto.GroupDTO;
import com.easylinkin.linkappapi.lobar.dto.LaborCompanyProjectDTO;
import com.easylinkin.linkappapi.lobar.dto.UserClockDTO;
import com.easylinkin.linkappapi.lobar.dto.UserRecordDTO;
import com.easylinkin.linkappapi.lobar.dto.app.AppUserGateDTO;
import com.easylinkin.linkappapi.lobar.dto.app.LobarUserDTO;
import com.easylinkin.linkappapi.lobar.dto.excel.ExcelResultDetailDTO;
import com.easylinkin.linkappapi.lobar.entity.ClockConfig;
import com.easylinkin.linkappapi.lobar.entity.Gate;
import com.easylinkin.linkappapi.lobar.entity.UserClock;
import com.easylinkin.linkappapi.lobar.entity.UserProject;
import com.easylinkin.linkappapi.lobar.entity.UserRecord;
import com.easylinkin.linkappapi.lobar.entity.UserStatistics;
import com.easylinkin.linkappapi.lobar.entity.emp.EmpUserBase;
import com.easylinkin.linkappapi.lobar.mapper.*;
import com.easylinkin.linkappapi.lobar.service.UserClockService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.lobar.service.UserProjectService;
import com.easylinkin.linkappapi.lobar.util.PercentageUtils;
import com.easylinkin.linkappapi.lobar.util.UserClockTimer;
import com.easylinkin.linkappapi.openapi.dto.systemDock.WorkAttendanceDTO;
import com.easylinkin.linkappapi.openapi.mapper.SystemDockInfoMapper;
import com.easylinkin.linkappapi.openapi.mapper.SystemDockingMapper;
import com.easylinkin.linkappapi.openapi.util.Base64Util;
import com.easylinkin.linkappapi.openapi.util.DataSyncUtil;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;

import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.*;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import site.morn.util.UUIDUtils;

/**
 * <p>
 * 闸机表 服务实现类
 * </p>
 *
 * @author kanyuanfeng
 * @since 2022-04-13
 */
@Service
public class UserClockServiceImpl extends ServiceImpl<UserClockMapper, UserClock> implements UserClockService {

    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;
    @Autowired
    private UserRecordMapper userRecordMapper;
    @Autowired
    private UserStatisticsMapper userStatisticsMapper;
    @Autowired
    private LaborCompanyProjectMapper laborCompanyProjectMapper;
    @Autowired
    private UserProjectService userProjectService;
    @Autowired
    private GroupMapper groupMapper;
    @Autowired
    private UserProjectMapper userProjectMapper;
    @Autowired
    private ClockConfigMapper clockConfigMapper;
    @Autowired
    private UserClockTimer userClockTimer;
    @Autowired
    private GateMapper gateMapper;

    @Resource
    private SystemDockingMapper dockingMapper;
    @Resource
    private SystemDockInfoMapper infoMapper;

    @Resource
    private EmpUserBaseMapper empUserBaseMapper;

    /**
     * 增加
     *
     * @param userClock
     */
    @Override
    public void insert(UserClock userClock) {
        /**
         * 验证重复
         */
//    this.checkExist(userClock);
        //设置基本属性
        //保存闸机区域 2022 10 21
        if (StringUtils.isNotBlank(userClock.getGateCode()) && null == userClock.getServiceArea()) {
            QueryWrapper<Gate> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("tenant_id_", userClock.getTenantId())
                    .eq("code_", userClock.getGateCode());
            List<Gate> gates = gateMapper.selectList(queryWrapper);
            userClock.setServiceArea(gates.get(0).getServiceArea());
            if (null == userClock.getDirection()) {
                userClock.setDirection(gates.get(0).getDirection());
            }
            //记录进入区域
            if (Integer.valueOf(1).equals(userClock.getDirection())) {
                userClock.setOnArea(gates.get(0).getServiceArea());
            } else {
                userClock.setOnArea(gates.get(0).getInArea());
            }
        }
        // 只有勾选计入考勤，才可以保存考勤数据(写入app_user_project和app_user_record表)
        // app_user_clock 表存所有的刷脸记录
        this.setBase(userClock);
        this.save(userClock);
        // 计入考勤
        if (Integer.valueOf(1).equals(userClock.getIsRecord())) {
            //计算改人员当前考勤
            computeTime(userClock);
            //同步考勤信息到东西湖智慧监管平台
            syncToSystem(userClock);
            //保存定位
            UpdateWrapper<UserProject> updateWrapper = new UpdateWrapper();
            updateWrapper.set("on_area_", userClock.getOnArea()).set("modify_time_", new Date()).set("clock_time_", new Date())
                    .eq("user_id_", userClock.getUserId()).eq("tenant_id_", userClock.getTenantId());
            userProjectMapper.update(null, updateWrapper);
        }
    }

    //计算改人员当前考勤
    private void computeTime(UserClock userClock) {
        //获取考勤规则
        ClockConfig clockConfig = getClockConfig(userClock.getTenantId());
        //获取时间
        Date begin = DateUtil.beginOfDay(userClock.getClockTime());
        Date end = DateUtil.endOfDay(userClock.getClockTime());
        //查询数据库是否有考勤记录
        UserRecord userRecord = new UserRecord();
        QueryWrapper<UserRecord> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id_", userClock.getUserId());
        wrapper.eq("tenant_id_", userClock.getTenantId());
        wrapper.ge("record_time_", begin);
        wrapper.le("record_time_", end);
        List<UserRecord> userRecords = userRecordMapper.selectList(wrapper);
        if (null != userRecords && userRecords.size() > 0) {
            userRecord = userRecords.get(0);
        } else {
            userRecord.setCreateTime(new Date());
            if (null != linkappUserContextProducer.getCurrent()) {
                userRecord.setCreatorId(linkappUserContextProducer.getCurrent().getId());
            }
            userRecord.setUserId(userClock.getUserId());
            userRecord.setTenantId(userClock.getTenantId());
            userRecord.setCompanyProjectId(userClock.getCompanyProjectId());
            userRecord.setGroupId(userClock.getGroupId());
        }
        userRecord.setType(2);
        userRecord.setModifyTime(new Date());
        if (null != linkappUserContextProducer.getCurrent()) {
            userRecord.setModifyId(linkappUserContextProducer.getCurrent().getId());
        }
        userRecord.setRecordTime(DateUtil.beginOfDay(userClock.getClockTime()));
        userRecord.setRecordType(0);
        //查询打卡记录
        QueryWrapper<UserClock> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id_", userClock.getUserId())
                .eq("tenant_id_", userClock.getTenantId())
                .and(i -> i.ne("is_record_", 0).or().isNull("is_record_"))
                .ge("clock_time", begin)
                .le("clock_time", end)
                .orderByAsc("clock_time");
        List<UserClock> clocks = baseMapper.selectList(queryWrapper);
        //设置半勤全勤
        if (null != clocks && clocks.size() > 0) {
            //出场卡
            List<Long> outList = clocks.stream()
                    .filter(c -> Integer.valueOf(2).equals(c.getDirection()))
                    .map(c -> c.getClockTime().getTime()).collect(Collectors.toList());
            //进场卡
            List<Long> inList = clocks.stream()
                    .filter(c -> Integer.valueOf(1).equals(c.getDirection()))
                    .map(c -> c.getClockTime().getTime()).collect(Collectors.toList());
            Integer countType = clockConfig.getCountType();
            Double hour = clockConfig.getHour();
            if (Integer.valueOf(1).equals(countType)) {
                //2022 12 06修改 出场减进场
                if (outList.size() < 1 || inList.size() < 1) {
                    userRecord.setHour(0d);
                } else {
                    Long max = outList.stream().reduce(outList.get(0), Long::max);
                    Long min = inList.stream().reduce(inList.get(0), Long::min);

//        List<Long> list = clocks.stream().map(c -> c.getClockTime().getTime())
//            .collect(Collectors.toList());
//        Long max = list.stream().reduce(list.get(0), Long::max);
//        Long min = list.stream().reduce(list.get(0), Long::min);
                    //求时间
                    float l = (float) (max - min) / 3600000f;
                    if (l < 0) {
                        l = 0f;
                    }
                    String format = new DecimalFormat("00.0").format(l);
                    userRecord.setHour(new Double(format));
                }
            } else {
                //考勤当天的0点时间
                Double aDouble = computeHour(clocks, begin, 0d);
                userRecord.setHour(aDouble);
            }
            //设置半勤全勤 2022 12 06
            if (inList.size() > 0 || userClock.getIsRecord() == 1) {
                userRecord.setRecordType(1);
                if (!Integer.valueOf(1).equals(clockConfig.getClockType())) {
                    userRecord.setRecordType(userRecord.getHour() >= hour ? 1 : 2);
                }
            }

            //设置半勤全勤
//      if (!Integer.valueOf(1).equals(clockConfig.getClockType())){
//        userRecord.setRecordType(userRecord.getHour()>=hour?1:2);
//      }else {
//        userRecord.setRecordType(1);
//      }
        }
//    //记录当前区域
//    userRecord.setOnArea(userClock.getOnArea());
        if (StringUtils.isNotBlank(userRecord.getId())) {
            userRecordMapper.updateById(userRecord);
        } else {
            //判断需不需要插入 防止高并发
            String uuid = IdUtil.simpleUUID();
            userRecord.setId(uuid);
            userRecordMapper.checkAndInsert(userRecord);
        }
    }

    private Double computeHour(List<UserClock> clocks, Date begin, Double d) {
        //时间段累加
        //第一次进场时间
        List<UserClock> joinClockList = clocks.stream()
                .filter(c ->
                        Integer.valueOf(1).equals(c.getDirection()) && begin.getTime() <= c.getClockTime().getTime())
                .sorted(Comparator.comparing(UserClock::getClockTime))
                .collect(Collectors.toList());
        if (joinClockList.size() > 0) {
            //查询离进场最近的一次出场
            UserClock userClock = joinClockList.get(0);
            //总数据排除这条
            clocks.removeIf(c -> userClock.getId().equals(c.getId()));
            Date clockTime = userClock.getClockTime();
            List<UserClock> outClockList = clocks.stream()
                    .filter(c ->
                            Integer.valueOf(2).equals(c.getDirection()) && clockTime.getTime() <= c.getClockTime().getTime())
                    .sorted(Comparator.comparing(UserClock::getClockTime))
                    .collect(Collectors.toList());
            if (outClockList.size() > 0) {
                UserClock clock = outClockList.get(0);
                clocks.removeIf(c -> clock.getId().equals(c.getId()));
                Date time = clock.getClockTime();
                float l = (float) (time.getTime() - clockTime.getTime()) / 3600000f;
                d += new Double(new DecimalFormat("00.0").format(l));
                return this.computeHour(clocks, time, d);
            }
        }
        return d;
    }

    /**
     * 获取考勤规则
     *
     * @return
     */
    private ClockConfig getClockConfig(String tenantId) {
        ClockConfig clockConfig;
        //查询今天之前的最新考勤规则,没有查到就用默认的
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, -1);
        QueryWrapper<ClockConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("tenant_id_", tenantId)
                .le("create_time_", DateUtil.endOfDay(calendar.getTime()))
                .orderByDesc("create_time_");
        List<ClockConfig> clockConfigs = clockConfigMapper.selectList(queryWrapper);
        if (null != clockConfigs && clockConfigs.size() > 0) {
            clockConfig = clockConfigs.get(0);
        } else {
            //查询默认的
            QueryWrapper<ClockConfig> queryWrapper2 = new QueryWrapper<>();
            queryWrapper2.isNull("tenant_id_");
            clockConfig = clockConfigMapper.selectList(queryWrapper2).get(0);
        }
        return clockConfig;
    }

    /**
     * 根据id编辑
     *
     * @param userClock
     */
    @Override
    public void updateOne(UserClock userClock) {
        /**
         * 验证重复
         */
        this.checkExist(userClock);
        //设置基本属性
        this.setBase(userClock);
        this.updateById(userClock);
        //同步考勤信息到东西湖智慧监管平台
        syncToSystem(userClock);
    }

    @Override
    public void delBatch(Set<String> ids) {
        ids.forEach(id -> {
            UserClock userClock = baseMapper.selectById(id);
            baseMapper.deleteById(id);
            this.computeTime(userClock);
        });
    }

    /**
     * @Description: 根据条件，分页(不分页)查询考勤报表
     * @author kan yuanfeng
     * @date 2020/11/04 11:42
     */
    @Override
    public IPage<UserClockDTO> queryRecordByPage(RequestModel<UserClockDTO> requestModel) {
        Page page = requestModel.getPage();
        UserClockDTO clockDTO = requestModel.getCustomQueryParams();
        //项目id 过滤
        clockDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        IPage<UserClockDTO> userClockIPage = baseMapper.queryRecordByPage(page, clockDTO);
        return userClockIPage;
    }

    /**
     * @Description: 根据条件，分页(不分页)查询考勤统计
     * @author kan yuanfeng
     * @date 2020/11/04 11:42
     */
    @Override
    public IPage<UserStatistics> queryStatistics(RequestModel<UserClockDTO> requestModel) {
        Page page = requestModel.getPage();
        UserClockDTO clockDTO = requestModel.getCustomQueryParams();
        //项目id 过滤
        clockDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        IPage<UserStatistics> userStatisticsIPage = userStatisticsMapper.queryStatistics(page, clockDTO);
        return userStatisticsIPage;
    }

    /**
     * @Description: 根据条件，分页(不分页)查询月视图
     * @author kan yuanfeng
     * @date 2020/11/04 11:42
     */
    @Override
    public IPage<UserClockDTO> queryMonthByPage(RequestModel<UserClockDTO> requestModel)
            throws InterruptedException, ExecutionException {
        Page page = requestModel.getPage();
        UserClockDTO clockDTO = requestModel.getCustomQueryParams();
        //计算总天数
        String[] split = clockDTO.getMouthStr().split("-");
        int lengthOfMonth = DateUtil
                .lengthOfMonth(Integer.valueOf(split[1]), DateUtil.isLeapYear(Integer.valueOf(split[0])));
        //项目id 过滤
        clockDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        IPage<UserClockDTO> userClockIPage = baseMapper.queryMonth(page, clockDTO);
        if (null != userClockIPage.getRecords() && userClockIPage.getRecords().size() > 0) {
            //查询该月的所有考勤
            //userId 排除
            Set<String> ids = userClockIPage.getRecords().stream().map(u -> u.getUserId())
                    .collect(Collectors.toSet());
            clockDTO.setIds(ids);
            List<UserClock> userClocks = baseMapper.findAllMonth(clockDTO);
            CompletionService<UserClockDTO> completionService = new ExecutorCompletionService(Executors
                    .newFixedThreadPool(20));
            userClockIPage.getRecords().forEach(r -> {
                completionService.submit(() -> setMounthList(lengthOfMonth, userClocks, r));
            });
            for (int i = 0; i < userClockIPage.getRecords().size(); i++) {
                UserClockDTO userClockDTO = completionService.take().get();
            }
        }
        return userClockIPage;
    }

    private UserClockDTO setMounthList(int lengthOfMonth, List<UserClock> userClocks,
                                       UserClockDTO r) {
        List<UserClock> list = userClocks.stream()
                .filter(u -> u.getUserId().equals(r.getUserId()))
                .collect(Collectors.toList());
        List<UserClockDTO> sonList = new ArrayList<>();
        SimpleDateFormat d = new SimpleDateFormat("d");
        int num = 0;
        for (int i = 1; i <= lengthOfMonth; i++) {
            UserClockDTO userClockDTO = new UserClockDTO();
            userClockDTO.setId(i + "");
            int finalI = i;
            List<UserClock> clockList = list.stream()
                    .filter(l -> Integer.valueOf(d.format(l.getClockTime())).equals(finalI))
                    .collect(Collectors.toList());
            if (clockList.size() > 0) {
                userClockDTO.setOnFlagStr("√");
                num++;
            }
            userClockDTO.setMouthList(BeanUtil.copyToList(clockList, UserClockDTO.class));
            sonList.add(userClockDTO);
        }
        r.setOnNum(num);
        r.setMouthList(sonList);
        return r;
    }

    /**
     * 最近5次考勤
     */
    @Override
    public List<UserClockDTO> findLastFive() {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        List<UserClockDTO> userClockDTOS = baseMapper.findLast(tenantId, 5);
        return userClockDTOS;
    }

    /**
     * 查询个人月考勤记录
     */
    @Override
    public List<UserRecord> getUserMouth(String userId, String mouth) {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        List<UserRecord> userRecords = userRecordMapper.getUserMouth(tenantId, userId, mouth);
        return userRecords;
    }

    /**
     * 根据实际查询当天打卡记录
     */
    @Override
    public List<UserClock> getUserDay(String userId, Date time) {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        QueryWrapper<UserClock> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("tenant_id_", tenantId)
                .and(i -> i.ne("is_record_", 0).or().isNull("is_record_"))
                .eq("user_id_", userId)
                .ge("clock_time", DateUtil.beginOfDay(time))
                .le("clock_time", DateUtil.endOfDay(time))
                .orderByAsc("clock_time");
        List<UserClock> userClocks = baseMapper.selectList(queryWrapper);
        return userClocks;
    }

    /**
     * 柱状图(7天考勤统计)
     */
    @Override
    public List<LobarUserDTO> getSevenDay() {

        //查询今天的
        LobarUserDTO sum = userProjectService.getSum(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        //查询近7天的数据
        List<UserStatistics> sevenAttendance = userStatisticsMapper
                .findSevenAttendance(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        //组装数据
        List<LobarUserDTO> lobarUserDTOS = new ArrayList<>();
        for (int i = 6; i >= 0; i--) {
            LobarUserDTO lobarUserDTO = new LobarUserDTO();
            if (i == 0) {
                lobarUserDTO.setRecordTime(new Date());
                lobarUserDTO.setSum(sum.getSum());
                lobarUserDTO.setAttendance(sum.getAttendance());
            } else {
                Calendar calendar = DateUtil.beginOfDay(Calendar.getInstance());
                calendar.add(Calendar.DAY_OF_YEAR, -i);
                List<UserStatistics> dtos = sevenAttendance.stream()
                        .filter(u -> u.getRecordTime().equals(calendar.getTime()))
                        .collect(Collectors.toList());
                if (dtos.size() > 0) {
                    lobarUserDTO.setAttendance(dtos.get(0).getOn());
                    lobarUserDTO.setSum(dtos.get(0).getSum());
                } else {
                    lobarUserDTO.setAttendance(0);
                    lobarUserDTO.setSum(0);
                }
                lobarUserDTO.setRecordTime(calendar.getTime());
            }
            lobarUserDTO.setPart(PercentageUtils.computePercentOne(lobarUserDTO.getAttendance(), lobarUserDTO.getSum()));
            lobarUserDTOS.add(lobarUserDTO);
        }
        return lobarUserDTOS;
    }

    @Override
    public IPage<UserClockDTO> queryListByPage(RequestModel<UserClockDTO> requestModel) {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        Page page = requestModel.getPage();
        UserClockDTO clockDTO = requestModel.getCustomQueryParams();
        //计算总天数
        String[] split = clockDTO.getMouthStr().split("-");
        int lengthOfMonth = DateUtil
                .lengthOfMonth(Integer.valueOf(split[1]), DateUtil.isLeapYear(Integer.valueOf(split[0])));
        //项目id 过滤
        clockDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
//    IPage<UserClockDTO> userClockIPage = baseMapper.queryListByPage(page, clockDTO);
        IPage<UserClockDTO> userClockIPage = baseMapper.queryList(page, clockDTO);
        if (null != userClockIPage.getRecords() && userClockIPage.getRecords().size() > 0) {
            //查询参建单位
            LaborCompanyProjectDTO laborCompanyProjectDTO = new LaborCompanyProjectDTO();
            laborCompanyProjectDTO.setTenantId(clockDTO.getTenantId());
            Page page1 = new Page();
            page1.setSize(-1);
            IPage<LaborCompanyProjectDTO> laborCompanyProjectDTOIPage = laborCompanyProjectMapper
                    .queryListByPage(page1, laborCompanyProjectDTO);
            List<LaborCompanyProjectDTO> records = laborCompanyProjectDTOIPage.getRecords();
            userClockIPage.getRecords().forEach(r -> {
                //查询月份
                r.setMouthStr(clockDTO.getMouthStr());
                //设置缺勤天数
                r.setOffNum(lengthOfMonth - r.getOnNum());
                //设置参建单位
                List<LaborCompanyProjectDTO> dtos = records.stream()
                        .filter(re -> r.getCompanyProjectId().equals(re.getCompanyProjectId()))
                        .collect(Collectors.toList());
                if (dtos.size() > 0) {
                    r.setCompanyName(dtos.get(0).getName());
                }
                // 查询最近考勤时间
//                if (r.getClockTime() == null) {
//                    String clockTime = baseMapper.queryClockTime((UserClockDTO) new UserClockDTO().setTenantId(tenantId).setUserId(r.getUserId()));
//                    SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//                    try {
//                        if (!StringUtils.isBlank(clockTime)) {
//                            r.setClockTime(ft.parse(clockTime));
//                        }
//                    } catch (ParseException e) {
//                        throw new RuntimeException(e);
//                    }
//                }
            });
        }
        return userClockIPage;
    }

    /**
     * app参建单位统计
     */
    @Override
    public List<AppUserGateDTO> getCompany(Date date) {
        List<AppUserGateDTO> appUserGateDTOS;
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        //判断是否今天
        DateTime time = DateUtil.beginOfDay(date);
        DateTime dateTime = DateUtil.beginOfDay(new Date());
        if (time.equals(dateTime)) {
            appUserGateDTOS = userRecordMapper.findCompanyToDay(tenantId, time, DateUtil.endOfDay(date));
        } else {
            appUserGateDTOS = userRecordMapper.findCompanyTime(tenantId, time, DateUtil.endOfDay(date));
        }
        appUserGateDTOS.forEach(a -> {
            a.setPart(PercentageUtils.computePercentOne(a.getAttendance(), a.getSum()));
        });
        return appUserGateDTOS;
    }

    /**
     * app班组统计
     */
    @Override
    public List<AppUserGateDTO> getGroup(String companyProjectId, Date date) {
        List<AppUserGateDTO> appUserGateDTOS;
        //判断是否今天
        DateTime time = DateUtil.beginOfDay(date);
        DateTime dateTime = DateUtil.beginOfDay(new Date());
        UserRecordDTO userRecordDTO = new UserRecordDTO();
        userRecordDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        userRecordDTO.setCompanyProjectId(companyProjectId);
        userRecordDTO.setStartTime(DateUtil.beginOfDay(date));
        userRecordDTO.setEndTime(DateUtil.endOfDay(date));
        if (time.equals(dateTime)) {
            appUserGateDTOS = userRecordMapper.findGroupToDay(userRecordDTO);
        } else {
            appUserGateDTOS = userRecordMapper.findGroupTime(userRecordDTO);
        }
        appUserGateDTOS.forEach(a -> {
            a.setPart(PercentageUtils.computePercentOne(a.getAttendance(), a.getSum()));
        });
        //排序
        return appUserGateDTOS.stream().sorted(Comparator.comparing(AppUserGateDTO::getAttendance).thenComparing(AppUserGateDTO::getSum).reversed())
                .collect(Collectors.toList());
    }

    /**
     * app人员统计
     */
    @Override
    public List<UserRecordDTO> getUser(String companyProjectId, String groupId, Date date, Integer recordType) {
        //判断是否今天
        DateTime time = DateUtil.beginOfDay(date);
        DateTime dateTime = DateUtil.beginOfDay(new Date());
        List<UserRecordDTO> userRecordDTOS;
        UserRecordDTO userRecordDTO = new UserRecordDTO();
        userRecordDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        userRecordDTO.setCompanyProjectId(companyProjectId);
        userRecordDTO.setGroupId(groupId);
        userRecordDTO.setStartTime(DateUtil.beginOfDay(date));
        userRecordDTO.setEndTime(DateUtil.endOfDay(date));
        userRecordDTO.setRecordType(recordType);
        if (time.equals(dateTime)) {
            userRecordDTOS = userRecordMapper.findUserToday(userRecordDTO);
        } else {
            userRecordDTOS = userRecordMapper.findUser(userRecordDTO);
        }
        return userRecordDTOS;
    }

    /**
     * 最近10次打卡记录
     */
    @Override
    public List<UserClockDTO> getTenClock() {
        List<UserClockDTO> userClockDTOS = baseMapper.getTenClock(linkappUserContextProducer.getNotNullCurrent().getTenantId(), 10);
        return userClockDTOS;
    }

    /**
     * 查询出勤人数最多的两个
     */
    @Override
    public List<LobarUserDTO> getBy2WorkType() {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
//    List<UserClockDTO> userClockDTOS =  baseMapper.getByToday(tenantId);
        List<UserRecordDTO> recordDTOS = userRecordMapper.getByToday(tenantId);
        Map<String, List<UserRecordDTO>> map = recordDTOS.stream()
                .filter(u->StringUtils.isNotBlank(u.getWorkType())).collect(Collectors.groupingBy(u -> u.getWorkType()));
        //取出最多的两个
        List<LobarUserDTO> lobarUserDTOS = new ArrayList<>();
        map.forEach((k, v) -> {
            LobarUserDTO lobarUserDTO = new LobarUserDTO();
            lobarUserDTO.setName(k);
            //出勤
            long attendance = v.stream().filter(va -> null != va.getRecordType() && !Integer.valueOf(0).equals(va.getRecordType())).count();
            lobarUserDTO.setAttendance((int) attendance);
            //在场
            long count = v.stream().filter(va -> null != va.getOnArea() && !Integer.valueOf(4).equals(va.getOnArea()))
                    .count();
            lobarUserDTO.setOn((int) count);
            lobarUserDTOS.add(lobarUserDTO);
        });
        List<LobarUserDTO> dtoList = lobarUserDTOS.stream()
                .sorted(Comparator.comparing(LobarUserDTO::getAttendance)
                        .reversed()).collect(Collectors.toList());
        if (dtoList.size() > 2) {
            return dtoList.subList(0, 2);
        } else {
            return dtoList;
        }
    }

    /**
     * 查询班组今日考勤数据
     */
    @Override
    public List<LobarUserDTO> getGroupClock() {
        //查询所有班组
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        //查询所有在场人员
        QueryWrapper<UserProject> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("tenant_id_", tenantId)
                .eq("status_", 1);
        List<UserProject> userProjects = userProjectMapper.selectList(queryWrapper);
        GroupDTO groupDTO = new GroupDTO();
        groupDTO.setTenantId(tenantId);
        groupDTO.setStatus(1);
        List<GroupDTO> groupDTOS = groupMapper.queryListByPage(groupDTO);
        //查询当天所有考勤
        List<UserRecordDTO> userRecordDTOS = userRecordMapper.getByToday(tenantId);
//    List<UserClockDTO> userClockDTOS = baseMapper.getByToday(tenantId);
        List<LobarUserDTO> result = new ArrayList<>();
        groupDTOS.forEach(g -> {
            //取班组考勤数据
            List<UserRecordDTO> recordDTOS = userRecordDTOS.stream()
                    .filter(u -> g.getId().equals(u.getGroupId()))
                    .collect(Collectors.toList());

            LobarUserDTO lobarUserDTO = new LobarUserDTO();
            lobarUserDTO.setGroupName(g.getName());
            lobarUserDTO.setCompanyName(g.getCompanyName());
//      List<UserClockDTO> dtos = userClockDTOS.stream()
//          .filter(u -> g.getId().equals(u.getGroupId()))
//          .collect(Collectors.toList());
            List<UserRecordDTO> dtos = recordDTOS.stream()
                    .filter(r -> null != r.getRecordType() && !Integer.valueOf(0).equals(r.getRecordType()))
                    .collect(Collectors.toList());
            lobarUserDTO.setAttendance(dtos.size());
            lobarUserDTO.setAttendanceUserIds(dtos.stream().map(UserRecordDTO::getUserId).collect(Collectors.toList()));
//      List<UserClockDTO> dtoList = dtos.stream()
//          .filter(d -> Integer.valueOf(1).equals(d.getDirection()))
//          .collect(Collectors.toList());
            List<UserRecordDTO> dtoList = recordDTOS.stream()
                    .filter(r -> null != r.getOnArea() && !Integer.valueOf(4).equals(r.getOnArea()))
                    .collect(Collectors.toList());
            lobarUserDTO.setOn(dtoList.size());
            lobarUserDTO.setOnUserIds(dtoList.stream().map(UserRecordDTO::getUserId).collect(Collectors.toList()));
            List<UserProject> projects = userProjects.stream()
                    .filter(u -> g.getId().equals(u.getGroupId()))
                    .collect(Collectors.toList());
            lobarUserDTO.setIn(projects.size());
            lobarUserDTO.setInUserIds(projects.stream().map(UserProject::getUserId).collect(Collectors.toList()));
            lobarUserDTO.setPart(PercentageUtils.computePercentOne(dtos.size(), projects.size()));
            result.add(lobarUserDTO);
        });
        return result;
    }

    /**
     * 查询作业人员考勤情况
     */
    @Override
    public Map<String, List<LobarUserDTO>> getTaskWorkType() {
        Map<String, List<LobarUserDTO>> result = new HashMap<>();

        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        //查询所有在场人员
        QueryWrapper<UserProject> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("tenant_id_", tenantId)
                .eq("status_", 1)
                .like("work_type_", "作业人员/");
        List<UserProject> userProjects = userProjectMapper.selectList(queryWrapper);
        Map<String, List<UserProject>> map = userProjects.stream().filter(u->StringUtils.isNotBlank(u.getWorkType())).collect(Collectors
                .groupingBy(u -> u.getWorkType()
                        .substring(u.getWorkType().indexOf("/") + 1, u.getWorkType().lastIndexOf("/"))));

        //查询出勤记录(总人日)
        LambdaQueryWrapper<UserRecord> reQueryWrapper = new LambdaQueryWrapper<>();
        reQueryWrapper.select(UserRecord::getId,UserRecord::getUserId)
                .eq(UserRecord::getTenantId, tenantId)
                .ne(UserRecord::getRecordType, 0);
        List<UserRecord> userRecords = userRecordMapper.selectList(reQueryWrapper);

        //今日出勤
//    List<UserClockDTO> byToday = baseMapper.getByToday(tenantId);
        List<UserRecordDTO> recordDTOS = userRecordMapper.getByToday(tenantId);
        map.forEach((k, v) -> {
            List<LobarUserDTO> list = new ArrayList<>();
            //再按工种进行分组
            Map<String, List<UserProject>> sonMap = v.stream().filter(u->StringUtils.isNotBlank(u.getWorkType())).collect(Collectors
                    .groupingBy(va -> va.getWorkType().substring(va.getWorkType().lastIndexOf("/") + 1)));
            sonMap.forEach((key, value) -> {
                //求userId集合
                Set<String> userIds = value.stream().map(u -> u.getUserId()).collect(Collectors.toSet());
                LobarUserDTO lobarUserDTO = new LobarUserDTO();
                lobarUserDTO.setName(key);
                //出勤
                List<String> userIds1 = recordDTOS.stream().filter(b -> userIds.contains(b.getUserId()) && null != b.getRecordType() && !Integer.valueOf(0).equals(b.getRecordType()))
                        .map(UserRecordDTO::getUserId).collect(Collectors.toList());
                lobarUserDTO.setAttendance(userIds1.size());
                lobarUserDTO.setAttendanceUserIds(userIds1);
                //在岗
                List<String> userIds2 = recordDTOS.stream().filter(b ->
                                userIds.contains(b.getUserId()) && null != b.getOnArea() && !Integer.valueOf(4).equals(b.getOnArea()))
                        .map(UserRecordDTO::getUserId).collect(Collectors.toList());
                lobarUserDTO.setOn(userIds2.size());
                lobarUserDTO.setOnUserIds(userIds2);
                //总人日
                long count3 = userRecords.stream().filter(u -> userIds.contains(u.getUserId())).count();
                lobarUserDTO.setSum((int) count3);
                lobarUserDTO.setIn(value.size());
                lobarUserDTO.setInUserIds(value.stream().map(UserProject::getUserId).collect(Collectors.toList()));
                list.add(lobarUserDTO);
            });
            result.put(k, list);
        });
        //总人日
        return result;
    }

    /**
     * 手动触发定时任务
     * type = 1 表示触发所有项目
     * 为空  本项目
     */
    @Override
    public void compute(Integer type, Date date) {
        userClockTimer.countClock(type, date);
    }

    /**
     * @Description: 根据条件，导出
     * @author kan yuanfeng
     * @date 2020/11/04 11:42
     */
    @Override
    public IPage<UserClockDTO> monthExport(RequestModel<UserClockDTO> requestModel)
            throws InterruptedException, ExecutionException {
        Page page = requestModel.getPage();
        UserClockDTO clockDTO = requestModel.getCustomQueryParams();
        //计算总天数
        String[] split = clockDTO.getMouthStr().split("-");
        int lengthOfMonth = DateUtil
                .lengthOfMonth(Integer.valueOf(split[1]), DateUtil.isLeapYear(Integer.valueOf(split[0])));
        //项目id 过滤
        clockDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        IPage<UserClockDTO> userClockIPage = baseMapper.queryMonth(page, clockDTO);
        if (null != userClockIPage.getRecords() && userClockIPage.getRecords().size() > 0) {
            //查询该月的所有考勤
            //userId 排除
            Set<String> ids = userClockIPage.getRecords().stream().map(u -> u.getUserId())
                    .collect(Collectors.toSet());
            clockDTO.setIds(ids);
//      List<UserRecord> userRecords = userRecordMapper.findAllMoth(clockDTO);
//      SimpleDateFormat d = new SimpleDateFormat("d");
            List<UserClock> userClocks = baseMapper.findAllMonthForExport(clockDTO);
            CompletionService<UserClockDTO> completionService = new ExecutorCompletionService(Executors
                    .newFixedThreadPool(20));
            userClockIPage.getRecords().forEach(r -> {
//        getMouthRecord(lengthOfMonth, userRecords, d, r);
                completionService.submit(() -> getMouthExport(lengthOfMonth, userClocks, r));
            });
            for (int i = 0; i < userClockIPage.getRecords().size(); i++) {
                completionService.take().get();
            }
        }
        return userClockIPage;
    }

    /**
     * @Description: 根据条件，分页(不分页)查询月视图（工时统计）
     * @author kan yuanfeng
     * @date 2020/11/04 11:42
     */
    @Override
    public IPage<UserClockDTO> queryMonthHourByPage(RequestModel<UserClockDTO> requestModel) throws Exception {
        Page page = requestModel.getPage();
        UserClockDTO clockDTO = requestModel.getCustomQueryParams();
        //计算总天数
        String[] split = clockDTO.getMouthStr().split("-");
        int lengthOfMonth = DateUtil
                .lengthOfMonth(Integer.valueOf(split[1]), DateUtil.isLeapYear(Integer.valueOf(split[0])));
        //项目id 过滤
        clockDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        IPage<UserClockDTO> userClockIPage = baseMapper.queryMonth(page, clockDTO);
        if (CollectionUtil.isNotEmpty(userClockIPage.getRecords())){
            //查询该月的所有考勤
            //userId 排除
            Set<String> ids = userClockIPage.getRecords().stream().map(u -> u.getUserId())
                    .collect(Collectors.toSet());
            clockDTO.setIds(ids);
            List<UserRecord> list = userRecordMapper.findHourMonth(clockDTO);
            //查询用户工时统计
            CompletionService<UserClockDTO> completionService = new ExecutorCompletionService(Executors
                    .newFixedThreadPool(20));
            userClockIPage.getRecords().forEach(r -> {
                completionService.submit(() -> setMounthHourList(lengthOfMonth, list, r));
            });
            for (int i = 0; i < userClockIPage.getRecords().size(); i++) {
                completionService.take().get();
            }
        }
        return userClockIPage;
    }

    /**
     * @Description: 根据条件，分页(不分页)查询人员考勤信息
     * @author kan yuanfeng
     * @date 2020/11/04 11:42
     */
    @Override
    public IPage<UserRecordDTO> findRecordByPage(RequestModel<UserClockDTO> requestModel) {
        //如果开始时间和结束时间都为null 则查询30天数据 含当天
        Page page = requestModel.getPage();
        UserClockDTO clockDTO = requestModel.getCustomQueryParams();
        clockDTO.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        if (null == clockDTO.getStartTime() && null == clockDTO.getEndTime()) {
            clockDTO.setStartTime(DateUtil.beginOfDay(DateUtil.offsetDay(new Date(), -30)));
            clockDTO.setEndTime(DateUtil.endOfDay(new Date()));
        }
        IPage<UserRecordDTO> record =userRecordMapper.findRecordByPage(page, clockDTO);
        if (CollectionUtil.isNotEmpty(record.getRecords())) {
            //查询参建单位
            LaborCompanyProjectDTO laborCompanyProjectDTO = new LaborCompanyProjectDTO();
            laborCompanyProjectDTO.setTenantId(clockDTO.getTenantId());
            Page page1 = new Page();
            page1.setSize(-1);
            IPage<LaborCompanyProjectDTO> laborCompanyProjectDTOIPage = laborCompanyProjectMapper
                    .queryListByPage(page1, laborCompanyProjectDTO);
            List<LaborCompanyProjectDTO> companyProjectDTOS = laborCompanyProjectDTOIPage.getRecords();
            record.getRecords().forEach(r -> {
                //设置参建单位
                List<LaborCompanyProjectDTO> dtos = companyProjectDTOS.stream()
                        .filter(re -> r.getCompanyProjectId().equals(re.getCompanyProjectId()))
                        .collect(Collectors.toList());
                if (dtos.size() > 0) {
                    r.setCompanyName(dtos.get(0).getName());
                }
            });
        }
        return record;
    }

    private UserClockDTO setMounthHourList(int lengthOfMonth, List<UserRecord> userRecords,
                                       UserClockDTO r) {
        List<UserRecord> records = userRecords.stream()
                .filter(u -> u.getUserId().equals(r.getUserId()))
                .collect(Collectors.toList());
        List<UserClockDTO> sonList = new ArrayList<>();
        SimpleDateFormat d = new SimpleDateFormat("d");
        for (int i = 1; i <= lengthOfMonth; i++) {
            UserClockDTO userClockDTO = new UserClockDTO();
            userClockDTO.setId(i + "");
            int finalI = i;
            List<UserRecord> records1 = records.stream()
                    .filter(l -> Integer.valueOf(d.format(l.getRecordTime())).equals(finalI))
                    .collect(Collectors.toList());
            if (records1.size() > 0) {
                userClockDTO.setHours(records1.get(0).getHour());
            }
            sonList.add(userClockDTO);
        }
        r.setMouthList(sonList);
        List<UserClockDTO> dtos = sonList.stream().filter(son -> null != son.getHours()).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(dtos)){
            double sum = dtos.stream().mapToDouble(dt -> dt.getHours()).sum();
            //平均工时
            r.setAvHours(sum/Integer.valueOf(dtos.size()).doubleValue());
            //总工时
            r.setSumHours(sum);
        }
        return r;
    }

    /**
     * 导出excel组织数据 2022 12 07
     *
     * @param lengthOfMonth
     * @param userClocks
     * @param r
     * @return
     */
    private UserClockDTO getMouthExport(int lengthOfMonth, List<UserClock> userClocks, UserClockDTO r) {
        SimpleDateFormat d = new SimpleDateFormat("d");
        List<UserClockDTO> sonList = new ArrayList<>();
        List<Date> dates = userClocks.stream().filter(u -> u.getUserId().equals(r.getUserId()))
                .map(u -> u.getClockTime()).collect(Collectors.toList());
        int num = 0;
        for (int i = 1; i <= lengthOfMonth; i++) {
            UserClockDTO userClockDTO = new UserClockDTO();
            userClockDTO.setId(i + "");
            int finalI = i;
            long count = dates.stream().filter(date -> Integer.valueOf(d.format(date)).equals(finalI))
                    .count();
            if (count > 0) {
                userClockDTO.setOnFlagStr("√");
                num++;
            }
            sonList.add(userClockDTO);
        }
        r.setOnNum(num);
        r.setMouthList(sonList);
        return r;
    }

    /**
     * 设置月视图打卡
     *
     * @param lengthOfMonth
     * @param userRecords
     * @param d
     * @param r
     */
    private UserClockDTO getMouthRecord(int lengthOfMonth, List<UserRecord> userRecords, UserClockDTO r) {
        SimpleDateFormat d = new SimpleDateFormat("d");
        List<UserClockDTO> sonList = new ArrayList<>();
        List<Date> dates = userRecords.stream().filter(u -> u.getUserId().equals(r.getUserId()))
                .map(u -> u.getRecordTime()).collect(Collectors.toList());
        for (int i = 1; i <= lengthOfMonth; i++) {
            UserClockDTO userClockDTO = new UserClockDTO();
            userClockDTO.setId(i + "");
            int finalI = i;
            long count = dates.stream().filter(date -> Integer.valueOf(d.format(date)).equals(finalI))
                    .count();
            if (count > 0) {
                userClockDTO.setOnFlagStr("√");
            }
            sonList.add(userClockDTO);
        }
        r.setMouthList(sonList);
        return r;
    }

    /**
     * 验证重复
     */
    private void checkExist(UserClock userClock) {
        QueryWrapper<UserClock> queryWrapper = new QueryWrapper<>();
        //todo 设置判断重复条件
        //编辑的时候存在id
        Optional.ofNullable(userClock.getId()).ifPresent(id -> queryWrapper.ne("id", userClock.getId()));
        Integer integer = baseMapper.selectCount(queryWrapper);
        if (integer > 0) {
            throw new BusinessException("该闸机已存在");
        }
    }

    /**
     * 设置基本属性
     *
     * @param userClock
     */
    private void setBase(UserClock userClock) {
        userClock.setModifyTime(new Date());
        if (null != linkappUserContextProducer.getCurrent()) {
            userClock.setModifyId(linkappUserContextProducer.getCurrent().getId());
        }
        //没有id就是新增,有就是编辑
        if (StringUtils.isBlank(userClock.getId())) {
            if (null != linkappUserContextProducer.getCurrent()) {
                userClock.setCreatorId(linkappUserContextProducer.getCurrent().getId());
            }
            userClock.setCreateTime(new Date());
            if (StringUtils.isBlank(userClock.getTenantId())) {
                userClock.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
            }
        }
    }

    /**
     * 同步考勤数据到东西湖智慧监管平台
     */
    private void syncToSystem(UserClock userClock) {

        new Thread(() -> {
            //根据用户id
            EmpUserBase emp = empUserBaseMapper.selectById(userClock.getUserId());

            if (emp != null) {
                WorkAttendanceDTO one = new WorkAttendanceDTO();
                one.setStaffIdNum(emp.getCard());
                one.setAttendStatus(userClock.getDirection());
                String area = "";
                //服务区域：1施工区，2办公区，3生活区
                if (userClock.getServiceArea() == 1) {
                    area = "施工区";
                } else if (userClock.getServiceArea() == 2) {
                    area = "办公区";
                } else {
                    area = "生活区";
                }
                one.setAttendPos(area);
                //不知逻辑，判断是否有图片路径，有就是人脸--算了 都是人脸
                one.setAttendType(1);
                List<String> list = new ArrayList<>();
                list.add(Base64Util.getImgStrToBase64(userClock.getPhoto()));
                one.setAttendPhoto(list);
                one.setAttendTime(com.easylinkin.linkappapi.common.utils.DateUtil.getYYYYMMDDHHMMSSDate(new Date()));

                String data = "[" + JSONObject.toJSONString(one) + "]";
                String url = "/assessHardware/staffAttend/saveOrUpdate";
                String tenantId =userClock.getTenantId();
                new Thread(new DataSyncUtil(dockingMapper, infoMapper, data, url, tenantId)).start();
            }
        }).start();

    }

}
